package com.yunsc.common.exception;


import cn.dev33.satoken.exception.NotLoginException;
import com.yunsc.common.utils.Result;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.multipart.MaxUploadSizeExceededException;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;

/**
 * @version: 1.0
 * @Author: adan
 * @description: 全局异常处理
 * @date: 2023-12-20 0:14
 */
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {

    /**
     * 处理请求参数校验异常
     */
    @ExceptionHandler(value = MethodArgumentNotValidException.class)
    public Result handleValidException(HttpServletRequest request, MethodArgumentNotValidException e) {
        // 打印详细信息
        logError(request,e);
        log.error("数据校验出现问题{},异常类型{}", e.getMessage(), e.getClass());
        BindingResult bindingResult = e.getBindingResult();
        HashMap<String, String> map = new HashMap<>();
        String defaultMessage = bindingResult.getFieldErrors().get(0).getDefaultMessage();
        bindingResult.getFieldErrors().forEach(item -> {
            String message = item.getDefaultMessage();
            String field = item.getField();
            map.put(field, message);
        });

        return Result.error(BizCodeEnum.VALID_EXCEPTION.getCode(), defaultMessage);
    }


    /**
     * 处理文件上传大小限制异常
     */
    @ExceptionHandler(value = MaxUploadSizeExceededException.class)
    public Result<?> handleMaxUploadSizeExceededException(MaxUploadSizeExceededException e) {
        // 打印详细信息
        log.error("文件上传大小超过限制，异常类型{}", e.getClass());
        return Result.error(500, "文件上传大小超过限制");
    }



    /**
     * 处理自定义异常
     */
    @ExceptionHandler(value = YunscException.class)
    public Result handleRRException(YunscException e) {
        log.error("YuanChuException异常，异常类型{};异常信息{}", e.getMsg(),e.getClass());
        return Result.error(e.getCode(), e.getMsg());
    }



    /**
     * 处理全局异常
     */
    @ExceptionHandler(value = Exception.class)
    public Result handleException(HttpServletRequest request, Exception e) {
        logError(request,e);
        log.error("未知异常，异常类型{}", e.getClass());
        return Result.error(BizCodeEnum.UNKNOWN_EXCEPTION.getCode(), BizCodeEnum.UNKNOWN_EXCEPTION.getMsg());
    }

    public void logError(HttpServletRequest request, Exception exception) {
        //换行符
        String lineSeparatorStr = System.getProperty("line.separator");

        StringBuilder exStr = new StringBuilder();
        StackTraceElement[] trace = exception.getStackTrace();
        // 获取堆栈信息并输出为打印的形式
        for (StackTraceElement s : trace) {
            exStr.append("\tat " + s + "\r\n");
        }
        //打印error级别的堆栈日志
        log.error("访问地址：" + request.getRequestURL() + ",请求方法：" + request.getMethod() +
                ",远程地址：" + request.getRemoteAddr() + lineSeparatorStr +
                "错误堆栈信息如下:" + exception.toString() + lineSeparatorStr + exStr);
    }


    // 全局异常拦截（拦截项目中的NotLoginException异常）
    @ExceptionHandler(NotLoginException.class)
    // @ResponseStatus(HttpStatus.UNAUTHORIZED)
    public Result handlerNotLoginException(NotLoginException nle) {
        // 打印堆栈，以供调试
        nle.printStackTrace();
        // 判断场景值，定制化异常信息
        String message = "";
        if(nle.getType().equals(NotLoginException.NOT_TOKEN)) {
            message = "请先登入";
            // message = "未能读取到有效 token";
        }
        else if(nle.getType().equals(NotLoginException.INVALID_TOKEN)) {
            message = "token 无效";
        }
        else if(nle.getType().equals(NotLoginException.TOKEN_TIMEOUT)) {
            message = "token 已过期";
        }
        else if(nle.getType().equals(NotLoginException.BE_REPLACED)) {
            message = "token 已被顶下线";
        }
        else if(nle.getType().equals(NotLoginException.KICK_OUT)) {
            message = "token 已被踢下线";
        }
        else if(nle.getType().equals(NotLoginException.TOKEN_FREEZE)) {
            message = "token 已被冻结";
        }
        else if(nle.getType().equals(NotLoginException.NO_PREFIX)) {
            message = "未按照指定前缀提交 token";
        }
        else {
            message = "当前会话未登录";
        }

        // 返回给前端
        return Result.error(HttpServletResponse.SC_UNAUTHORIZED, message);
    }


}
